
哈囉大家好~
不知道昨天的進度條做的怎麼樣?
想要交作業的人可以貼在昨天的留言區給我呦!
那我們今天的內容也很簡單,
二話不說,要開始囉?
 前置作業
 前置作業跟昨天的環境相同,兔兔為了寫文章方便是重新建立了一個,你們可以重新建立新的專案從頭練習一次,也可以在昨天做完的專案直接多增加新的元件哦!
 
 建立空白元件
 建立空白元件和昨天一樣,先建立空白元件,所以在專案裡的 ./src/components 資料夾中新增一個 RabbitButton.vue 的元件:

完成後,增添以下內容:
<template>
  
</template>
<script>
export default {
  name: "RabbitButton",
}
</script>
接著我們就可以把這個元件新增到畫面中,打開 App.vue 然後內容跟昨天差不多:
<template>
  <div :class="[
      'w-screen h-screen',
      'flex flex-col',
      'justify-center items-center',
      'gap-5',
    ]"
  >
    <RabbitButton />
  </div>
</template>
<script>
import RabbitButton from './components/RabbitButton.vue'
export default {
  data() {
    return {
      
    }
  },
  components: {
    RabbitButton,
  }
}
</script>
這樣準備工作大妥當,開始按鈕博物館導覽啦!
 
 按鈕
 按鈕按鈕的要素其實很簡單,不外乎就是一個中間有字的矩形,滑鼠懸停時有顏色變化,最好的話連按下後也有相對應的效果,才是一個良好的互動。
那麼基於這些要素,我們就可以這麼做:
px-6、py-3
rounded-md、font-bold
bg-gray-400、hover:bg-gray-300、active:bg-gray-500
hover:scale-105、active:scale-95
transition-all
按鈕 二字讓它不會被誤認吧?所以綜合起來,就會像是這樣:
<template>
  <div
    :class="[
      'px-6 py-3',
      'rounded-md font-bold',
      'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
      'hover:scale-105 active:scale-95',
      'transition-all'
    ]"
  >
    按鈕
  </div>
</template>
<script>
export default {
  name: "RabbitButton",
}
</script>


這麼快就有 fu 了嗎?
哎呀後面還有更有趣的啦!
畢竟我們現在按鈕還非常的單調,
讓我們來幫它點綴一下!
 
 動起來
 動起來Yes,這麼快就來到了我們動起來的環節了。
別以為會這麼輕易地結束,
我們現在看到的按鈕是這個樣子:
可是兔兔期待的是這個樣子:
哇,還是有點落差對吧?
「摁,是沒錯啦,但不就是差個 icon 和顏色而已嘛 ... 不過每次插入 svg 的 icon 都會覺得把結構弄得好雜亂 ...」
嘿嘿,這個立馬幫你解決!
沒錯,這就是輪到不知道甚麼時候才能派上用場但現在就派上用場的 Slot 啦!
slot 可以把原本一直要替換的東西從內部解藕出來,那麼一來我們在外面替換時就不用一直修改元件的內部功能了~
現在在文字前後幫按鈕加上兩個 slot,分別取名為 iconLeft 和 iconRight,然後因為想要 icon 和文字可以橫向排列且之間有點空隙,我們順便加上 flex gap-2 的樣式:
<!-- RabbitButton.vue -->
<div
  :class="[
    'flex gap-2',
    'px-6 py-3',
    'rounded-md font-bold',
    'text-white',
    'bg-blue-400 hover:bg-blue-300 active:bg-blue-500',
    'hover:scale-105 active:scale-95',
    'transition-all'
  ]"
>
  <slot name="iconLeft" />
  按鈕
  <slot name="iconRight" />
</div>
這麼一來就可以輕鬆的在按鈕文字的前或後加上 icon 啦!
然後啊,Tailwind 官方的專案中有個特別的網站,叫做 heroicons,雖然裡面的 icon 量真的不多,但是各個都是精心設計過,適合搭配 Tailwind 直接使用的 icon,還可以直接複製 svg,算是很方便啦~

我們回到 App.vue,兔兔這邊要用的 icon 就是在 heroicons 上面找的 wifi icon (不是 wife,要認明。),然後包在指定 iconLeft 插槽 的 template 中:
<!-- App.vue -->
<div 
  :class="[
    'w-screen h-screen',
    'flex flex-col',
    'justify-center items-center',
    'gap-5',
  ]"
>
  <RabbitButton>
    <template v-slot:iconLeft>
      <svg  xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" />
      </svg>
    </template>
  </RabbitButton>
</div>

讚啦,這樣真的很方便,把 icon 從元件中抽離之後就可以隨時替換了!
可是 ...(你這兔又來了,果然不管做的怎麼樣都有話要說!)
就是真的需要提出來啦!
按鈕的文字和顏色的改變的確是還不方便,所以我們就必須用到 ...
對,就是 Props。
我們剛剛說到要改變文字和顏色,所以就幫 props 增加兩個內容,但這兩個內容都必須要有預設值:
<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
}
</script>
我們先來解決按鈕文字的部分,把按鈕的字樣取代成 {{ text }}:
<div
  :class="[
    'flex gap-2',
    'px-6 py-3',
    'rounded-md font-bold',
    'text-white',
    'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
    'hover:scale-105 active:scale-95',
    'transition-all'
  ]"
>
  <slot name="iconLeft" />
  {{ text }}
  <slot name="iconRight" />
</div>
再來,就要解釋到為什麼 color 要有預設值且預設值是字串了。 因為我們要利用物件 (字典) 的特性來快速的切換按鈕的色系!
為了方便監控和節省資源,我們先在 computed 中建立一個 colorSelector 函數,並返回一個空白物件。
<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
  computed: {
    colorSelector() {
      return {}
    }
  }
}
</script>
建立好之後,我們把元件中顏色的那一行 tailwind 語法加到物件中並命名為 gray,且在物件後加上 [this.color] 來讓函數找到並返回指定名稱為 gray 的內容:
<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
  computed: {
    colorSelector() {
      return {
        gray: 'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
      }[this.color]
    }
  }
}
</script>
然後把 colorSelector 函數加回 class 列表,並測試一次:
<div
  :class="[
    'flex gap-2',
    'px-6 py-3',
    'rounded-md font-bold',
    'text-white',
    colorSelector,
    'hover:scale-105 active:scale-95',
    'transition-all'
  ]"
>
  <slot name="iconLeft" />
  {{ text }}
  <slot name="iconRight" />
</div>

看起來沒什麼變化是正常的,但至少這表示沒寫錯!
打鐵要趁熱,我們接著來追加其他顏色吧~
最後追加完,完整的 code 可能會是這個樣子:
<template>
  <div
    :class="[
      'flex gap-2',
      'px-6 py-3',
      'rounded-md font-bold',
      'text-white',
      colorSelector,
      'hover:scale-105 active:scale-95',
      'transition-all'
    ]"
  >
    <slot name="iconLeft" />
    {{ text }}
    <slot name="iconRight" />
  </div>
</template>
<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
  computed: {
    colorSelector() {
      return {
        gray: 'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
        green: 'bg-green-400 hover:bg-green-300 active:bg-green-500',
        blue: 'bg-blue-400 hover:bg-blue-300 active:bg-blue-500',
        red: 'bg-red-400 hover:bg-red-300 active:bg-red-500',
        yellow: 'bg-yellow-400 hover:bg-yellow-300 active:bg-yellow-500',
      }[this.color]
    }
  }
}
</script>
然後這邊值得注意的是,為什麼不用變數取代 bg-XXX-400、hover:bg-XXX-300 就好了呢? 為何還要這樣大費周章地去把 class 名稱都寫過一遍?
因為要讓 purge 讀取到要編譯的 class 的話,class 名稱必須保持完整,不能被拆開!
所以為了要讓樣式能正確被讀取到且被編譯,不能使用 "bg-" + color + "500" 或是 "opacity-" + number 這種組合過的形式,這對 Tailwind 來說是不合法的 class 名稱哦。
既然完成了,我們就快點來測試吧!
好興奮啊!

 最後測試
 最後測試最後的測試環境,我們來做三個按鈕吧!
然後為了美化中的美化,我們可以在按鈕元件中加上 cursor-pointer 讓它看起來更像樣!
第一個按鈕,就是最普通的按鈕啦!
<RabbitButton />

再來是讓人看起來很連網路的 wifi 按鈕!
<RabbitButton text="wifi" color="blue">
  <template v-slot:iconLeft>
    <svg  xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" />
    </svg>
  </template>
</RabbitButton>

最後,是安裝軟體時會出現的下一步按鈕~
<RabbitButton text="下一步" color="green">
  <template v-slot:iconRight>
    <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
    </svg>
  </template>
</RabbitButton>

OK,超成功的啦!
 
今天是不是也很輕鬆簡單啊?
兔兔覺得這樣做按鈕又美、彈性又高!
以後遇到再多狀況也只要小修改就能夠繼續使用了,
這有再度登場的機會正是做元件的目的!
希望你們都能夠試試看,如果有更好方法或建議 ...
做成作業留言給我看嘿!!!!
 
 給你們的回家作業:
 給你們的回家作業:關於兔兔們:

 
( # 兔兔小聲說 )
不是兔兔偷懶,兔兔只是陪富堅去取材。